#include "semaphore.h"

#include "osCpu.h"


#if OS_ENABLE_SEMAPHORE == 1          


/*****************************************************************************
 * @function        vSemaphoreInit
 * @brief 			初始化信号量.
 *  
 * @param[in] 		sem 待初始化的信号量.
 * @param[in]       startCount 信号量初始计数值.
 * @param[in] 	    maxCount  信号量的最大计数值, 此值为0表示不限制计数值的大小.
 * 
 * @return 			None.   
 * @note 			初始计数值>最大计数值=最大计数值. 最大计数值=0--> 计数值不限制	
 *
 * ***************************************************************************/
void     vSemaphoreInit(SEMAPHORE_T *sem, uint32_t startCount, uint32_t maxCount)
{
    vEventInit(&sem->event, eventTypeSem);
    sem->maxCount = maxCount;
    
    if (0 == maxCount)
    {
        sem->count = startCount;        // 不限制计数值
    }
    else
    {
        sem->count = (startCount > maxCount) ? maxCount : startCount;       // 限制在maxcount以内
    }
}

/*****************************************************************************
 * @function        vSemaphoreGetCount
 * @brief 			获取信号量当前的计数值.
 *  
 * @param[in] 		sem 信号量.
 * 
 * @return 			信号量当前的计数值.
 *
 * ***************************************************************************/
uint32_t vSemaphoreGetCount(SEMAPHORE_T *sem)
{
    uint32_t count;
    uint32_t primask = vTaskEnterCritical();
    
    count = sem->count;
    
    vTaskExitCritical(primask);
    return count;
}



/*****************************************************************************
 * @function        vSemaphoreWait
 * @brief 			等待信号量
 *  
 * @param[in] 		sem 信号量
 * @param[in]       timeout 任务等待时间
 * 
 * @return 			返回事件结果.
 *
 * ***************************************************************************/
uint32_t   vSemaphoreWait(SEMAPHORE_T *sem, uint32_t timeout)  
{
    uint32_t primask = vTaskEnterCritical();
    if (sem->count > 0)
    {
        sem->count--;
        vTaskExitCritical(primask);
        return OS_NOERROR;
    }
    else {
        /* 无计数值, 任务进入事件队列中等待*/
        vEventWait(&sem->event, curTask, (void *)0, OS_TASK_STA_WAIT_SEM, timeout);
        vTaskExitCritical(primask);
        
        /* 进行任务切换*/
        vTaskYield();
        
        /* 当任务从等待超时或者计数值可用时，会执行到此，取出等待结果*/
        return curTask->waitEventResult;
    }
}

/*****************************************************************************
 * @function        vSemaphoreNoWaitGet
 * @brief 			获取信号量(不等待)
 *  
 * @param[in] 		sem 信号量
 * 
* @return 			OS_NOERROR: 获取计数值成功; OS_ERROR_NORESOURCE: 无信号量资源.
 *
 * ***************************************************************************/
uint32_t vSemaphoreNoWaitGet (SEMAPHORE_T *sem)
{
    uint32_t primask = vTaskEnterCritical();
    
    /*  信号量有计数值，计数值-1返回*/
    if (sem->count > 0)
    {
        sem->count--;
        
        vTaskExitCritical(primask);
        return OS_NOERROR;
    }
    /* 无计数值，直接返回*/
    else {
        vTaskExitCritical(primask);
        return OS_ERROR_NORESOURCE;
    }
}



/*****************************************************************************
 * @function        vSemaphoreGetCount
 * @brief 			等待信号量
 *  
 * @param[in] 		sem 信号量
 * @param[in]       task 要获取信号量的任务
 * @param[in]       timeout 任务等待时间
 * 
 * @return 			None.
 *
 * ***************************************************************************/
void     vSemaphoreNotify(SEMAPHORE_T *sem)
{
    TASK_T *task;
    uint32_t primask = vTaskEnterCritical();
    
    /* 检查是否有任务等待*/
    if (vEventWaitCount(&sem->event) > 0)
    {
        /* 有任务在等待-- 进行唤醒*/
        task = vEventWakeUp(&sem->event, (void *)0, 0);
        
        vTaskExitCritical(primask);
        // 事件任务比当前任务优先级高
        if (task->prio < curTask->prio)
        {
            vTaskYield();
        }
        
        return ;
    }
    else 
    {
        /* 无任务等待, 计数值+1*/
        sem->count ++;
        
        /* 超过最大计数值，进行限制*/
        if ( (sem->maxCount != 0) && (sem->count > sem->maxCount))
        {
            sem->count = sem->maxCount;
        }
        
        vTaskExitCritical(primask);
    }
    
    
}


/*****************************************************************************
 * @function        vSemaphoreDestroy
 * @brief 			删除信号量. 包括两部分，1删除信号量中的事件等待任务，2将计数值清零
 *  
 * @param[in] 		sem 信号量
 * 
 * @return 			返回被清除的任务个数.
 *
 * ***************************************************************************/
uint32_t vSemaphoreDestroy(SEMAPHORE_T *sem)
{
    uint32_t count;
    
    uint32_t primask = vTaskEnterCritical();
    
    count = vEventRemoveAll(&sem->event, (void *)0, OS_FORCE_DEL);
    
    sem->count = 0;
    vTaskExitCritical(primask);
    
    /* 事件中存在任务，需要进行任务调度*/
    if (count > 0)
    {
        vTaskYield();
    }
    
    return count;

}


/*****************************************************************************
 * @function        vSemaphoreInfo
 * @brief 			获取信号量相关信息. 
 *  
 * @param[in] 		sem  信号量
 * @param[out]      info 信号量信息结构
 * 
 * @return 			None.
 *
 * ***************************************************************************/
void     vSemaphoreInfo(SEMAPHORE_T *sem, SEMAPHORE_INFO_T *info)
{
    uint32_t primask = vTaskEnterCritical();
    
    info->count = sem->count;
    info->maxCount = sem->maxCount;
    info->taskCount = vEventWaitCount(&sem->event);
    
    vTaskExitCritical(primask);
}

#endif // OS_ENABLE_SEMAPHORE == 1

